package com.thread.threadfirst.encrypt;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA 加解密
 */
public class RSAOperator {
    private static final String KEY_ALGORITHM = "RSA";
    private static final String PUBLIC_KEY ="publicKey";
    private static final String PRIVATE_KEY ="privateKey";

    public static void main(String[] args) throws Exception{
        Map<String,String> keyMap = genKey();
        RSAPublicKey publicKey = getPublicKey(keyMap.get(PUBLIC_KEY));
        RSAPrivateKey privateKey = getPrivateKey(keyMap.get(PRIVATE_KEY));
//        System.out.println(publicKey.toString());

        String info ="明文123456";
        //加密
        byte[] encryptData = encrypt(info.getBytes("utf-8"),publicKey);
        String encodeBase64Data = Base64.encodeBase64String(encryptData);//用publicKey加密后的密文需要编码成Base64字符串。
//        System.out.println("密文：" + new String(encryptData,"utf-8"));
        System.out.println("加密后：" + encodeBase64Data);
        //解密
//        byte[] decryptData = decrypt(encryptData, privateKey);
        byte[] decryptData = decrypt(Base64.decodeBase64(encodeBase64Data), privateKey);
        System.out.println("明文：" + new String(decryptData,"utf-8"));

    }

    public static Map<String,String> genKey() throws NoSuchAlgorithmException {
        Map<String,String> keyMap = new HashMap<String,String>();
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom random = new SecureRandom();
        // random.setSeed(keyInfo.getBytes());
        /**
         * jdk8: key必须在[512,1024]之间,并且是64的倍数。有的JDK版本要求是8的倍数，这要根据实际情况和需求设定
         *
         * 初始加密，512位已被破解，用1024位,最好用2048位
         */
        keygen.initialize(1024, random);
        // 取得密钥对
        KeyPair kp = keygen.generateKeyPair();//第一次调用generateKeyPair()都会生成不同的KeyPair。KeyPairGenerator 每次生成的都是一个KeyPair。
        RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
//        String privateKeyString = Base64.encode(privateKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(privateKey.getEncoded());
        RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
        String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
        keyMap.put(PUBLIC_KEY, publicKeyString);
        keyMap.put(PRIVATE_KEY, privateKeyString);
        return keyMap;
    }

    public static RSAPublicKey getPublicKey(String publicKey) throws Exception{
//        byte[] keyBytes = LBase64.decode(publicKey);
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);//Only RSAPublicKeySpec and X509EncodedKeySpec supported for RSA public keys
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }

    public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception{
//        byte[] keyBytes = LBase64.decode(privateKey);
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);//Only RSAPrivate(Crt)KeySpec and PKCS8EncodedKeySpec supported for RSA private keys
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
    }

    public static byte[] encrypt(byte[] context,RSAPublicKey rsaPublicKey) throws Exception {
        Cipher cipher=Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE,rsaPublicKey);
        byte[] encryptData = cipher.doFinal(context);
        return encryptData;
    }

    public static byte[] decrypt(byte[] context,RSAPrivateKey rsaPrivateKey) throws Exception {
        Cipher cipher=Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE,rsaPrivateKey);
        byte[] encryptData = cipher.doFinal(context);
        return encryptData;
    }
}
